#include "DX_APP.h"
#include "../Trivial_PS.csh"
#include "../VertexShader.csh"
#include "../SimpleVertex.csh"
#include "../PixelShader.csh"
#include "../PlaneGShader.csh"
#include "../SkyBox_PS.csh"
#include "../SkyBox_VS.csh"
#include "D3DMesh.h"
#include "ShaderGroup.h"
#include <thread>
#include <mutex>



DX_APP::DX_APP(HINSTANCE hinst, WNDPROC proc)
{
	application = hinst;
	appWndProc = proc;

	WNDCLASSEX  wndClass;
	ZeroMemory(&wndClass, sizeof(wndClass));
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.lpfnWndProc = appWndProc;
	wndClass.lpszClassName = L"DirectXApplication";
	wndClass.hInstance = application;
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOWFRAME);
	//wndClass.hIcon			= LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_FSICON));
	RegisterClassEx(&wndClass);

	RECT window_size = { 0, 0, (LONG)BACKBUFFER_WIDTH, (LONG)BACKBUFFER_HEIGHT };
	AdjustWindowRect(&window_size, WS_OVERLAPPEDWINDOW, false);

	HWND window = CreateWindow(L"DirectXApplication", L"Lab 1a Line Land", WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX),
		100, 100, window_size.right - window_size.left, window_size.bottom - window_size.top,
		NULL, NULL, application, this);

	ShowWindow(window, SW_SHOW);
	

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(sd));
	sd.BufferCount = 1;
	sd.BufferDesc.Width = (UINT)BACKBUFFER_WIDTH;
	sd.BufferDesc.Height = (UINT)BACKBUFFER_HEIGHT;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = window;
	sd.SampleDesc.Count = 8;
	sd.SampleDesc.Quality = 16;
	sd.Windowed = TRUE;
	sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;



	UINT flag = 0;

	// TODO: PART 1 STEP 3b
	HRESULT hr = S_OK;
	D3D_FEATURE_LEVEL Features[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0

	};

	D3D_FEATURE_LEVEL feature;

	


#if _DEBUG

	flag = D3D11_CREATE_DEVICE_DEBUG;
#endif

	hr = D3D11CreateDeviceAndSwapChain(NULL,
		D3D_DRIVER_TYPE_HARDWARE,
		NULL,
		flag,
		Features,
		4,
		D3D11_SDK_VERSION,
		&sd,
		&pSwapChain,
		&pDevice,
		&feature,
		&pContext
		);

	hr = S_OK;
	// TODO: PART 1 STEP 4
	ID3D11Resource* pBB = 0;
	hr = pSwapChain->GetBuffer(0, __uuidof(pBB), reinterpret_cast<void**>(&pBB));

	hr = S_OK;
	

	hr = pDevice->CreateRenderTargetView(pBB, NULL, &pRenderTargetView);

	if (pBB) pBB->Release();

	// TODO: PART 1 STEP 5
	hr = pSwapChain->GetDesc(&sd);
	ZeroMemory(&viewPort, sizeof(viewPort));
	viewPort.TopLeftX = 0;
	viewPort.TopLeftY = 0;
	viewPort.Width = (FLOAT)sd.BufferDesc.Width;;
	viewPort.Height = (FLOAT)sd.BufferDesc.Height;;
	viewPort.MinDepth = 0;
	viewPort.MaxDepth = 1;
	

	hr = CreateDepthStencilBuffer();

	//CD3D11_RASTERIZER_DESC cwCullDesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_MODE::D3D11_FILL_SOLID, D3D11_CULL_MODE::D3D11_CULL_BACK, false,;
	

	D3D11_RASTERIZER_DESC rasterDesc1;
	ZeroMemory(&rasterDesc1, sizeof(rasterDesc1));

	rasterDesc1.FillMode = D3D11_FILL_SOLID;
	rasterDesc1.CullMode = D3D11_CULL_BACK;
	rasterDesc1.DepthClipEnable = TRUE;
	rasterDesc1.MultisampleEnable = FALSE;
	rasterDesc1.AntialiasedLineEnable = FALSE;

	pDevice->CreateRasterizerState(&rasterDesc1, &CCWCull);

	D3D11_RASTERIZER_DESC rasterDesc2;
	ZeroMemory(&rasterDesc2, sizeof(rasterDesc2));

	rasterDesc2.FillMode = D3D11_FILL_SOLID;
	rasterDesc2.CullMode = D3D11_CULL_BACK;
	rasterDesc2.FrontCounterClockwise = true;
	rasterDesc2.DepthClipEnable = TRUE;
	rasterDesc2.MultisampleEnable = FALSE;
	rasterDesc2.AntialiasedLineEnable = FALSE;

	pDevice->CreateRasterizerState(&rasterDesc2, &CWCull);

	shaderSet1.InitializeShaders(pDevice, Trivial_PS, sizeof(Trivial_PS), VertexShader, sizeof(VertexShader));
	starShaders.InitializeShaders(pDevice, PixelShader, sizeof(PixelShader), SimpleVertex, sizeof(SimpleVertex));

	planeShaders.InitializeShaders(pDevice, Trivial_PS, sizeof(Trivial_PS), SimpleVertex, sizeof(SimpleVertex), PlaneGShader, sizeof(PlaneGShader));
	skyBoxShaders.InitializeShaders(pDevice, SkyBox_PS, sizeof(SkyBox_PS), SkyBox_VS, sizeof(SkyBox_VS));

	

#pragma region StarSetUp
		unsigned int indicies[60];
		VERTEX verticies[12];
		
	
	
		float deltarRads = (3.1459f* 2) / 5;
		float rads = 3.1459f * 0.5f;
	
		for (unsigned int outPoint = 0; outPoint < 5; outPoint++)
		{
			verticies[outPoint].pos.x = cos(rads);
			verticies[outPoint].pos.y = sin(rads);
			verticies[outPoint].pos.z = 0.0f;
	
			rads += deltarRads;
		}
		rads = 3.1459f * 0.25f;
	
		for (unsigned int inPoint = 5; inPoint < 10; inPoint++)
		{
			verticies[inPoint].pos.x = cos(rads) * .5f;
			verticies[inPoint].pos.y = sin(rads) * .5f;
			verticies[inPoint].pos.z = 0.0f;
	
			rads += deltarRads;
		}
	
		verticies[10].pos.x = 0.0f;
		verticies[10].pos.y = 0.0f;
		verticies[10].pos.z = 0.2f;
	
		verticies[11].pos.x = 0.0f;
		verticies[11].pos.y = 0.0f;
		verticies[11].pos.z = -0.2f;
	
		verticies[0]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[1]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[2]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[3]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
							
		verticies[4]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[5]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[6]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[7]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
							 
		verticies[8]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[9]._color = { 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[10]._color ={ 1.0f, 1.0f, 0.0f, 1.0f };
		verticies[11]._color ={ 1.0f, 1.0f, 0.0f, 1.0f };
	
	
		//Top Point
		indicies[0] = 0;
		indicies[1] = 5;
		indicies[2] = 11;
		indicies[3] = 0;
		indicies[4] = 11;
		indicies[5] = 6;
		//Top Right
		indicies[6] = 11;
		indicies[7] = 5;
		indicies[8] = 4;
		indicies[9] = 11;
		indicies[10] = 4;
		indicies[11] = 9;
		//Bottom right
		indicies[12] = 11;
		indicies[13] = 9;
		indicies[14] = 3;
		indicies[15] = 11;
		indicies[16] = 3;
		indicies[17] = 8;
	
		//Bottom left
		indicies[18] = 11;
		indicies[19] = 8;
		indicies[20] = 2;
		indicies[21] = 11;
		indicies[22] = 2;
		indicies[23] = 7;
	
		//Top left
		indicies[24] = 11;
		indicies[25] = 7;
		indicies[26] = 1;
		indicies[27] = 11;
		indicies[28] = 1;
		indicies[29] = 6;
	
		//Top left
		indicies[30] = 10;
		indicies[31] = 0;
		indicies[32] = 6;
		indicies[33] = 10;
		indicies[34] = 5;
		indicies[35] = 0;
	
		indicies[36] = 10;
		indicies[37] = 6;
		indicies[38] = 1;
		indicies[39] = 10;
		indicies[40] = 1;
		indicies[41] = 7;
	
		indicies[42] = 10;
		indicies[43] = 7;
		indicies[44] = 2;
		indicies[45] = 10;
		indicies[46] = 2;
		indicies[47] = 8;
	
		indicies[48] = 10;
		indicies[49] = 8;
		indicies[50] = 3;
		indicies[51] = 10;
		indicies[52] = 3;
		indicies[53] = 9;
	
		indicies[54] = 10;
		indicies[55] = 9;
		indicies[56] = 4;
		indicies[57] = 10;
		indicies[58] = 4;
		indicies[59] = 5;

#pragma endregion
		//star.CreateFromVectors(pDevice, verticies, indicies, 12, 60, { 1.0f, 1.0f, 1.0f, 1.0f });

		unsigned int ind = 0;
		VERTEX vert;
		vert.pos = { 0.0f, 0.0f, 0.0f };
		vert.normal = { 0.0f, 1.0f, 0.0f };
		vert.uv = { 0.0f, 0.0f };
		vert._color = { 1.0f, 1.0f, 1.0f, 1.0f }; 
		
	//	plane.CreateFromVectors(pDevice, &vert, indicies, 6, 0, { 1.0f, 1.0f, 1.0f, 1.0f }, "resources/DesertTexture.dds");
//		CreateStarInstanceBuffer();


		std::thread thread1(&D3DMesh::Initizalize, &alien, pDevice, "resources/MPHybrid.obj", "resources/alienTexture.dds", false);
		std::thread thread2(&D3DMesh::Initizalize, &SkyBox, pDevice, "resources/Cube.obj", "resources/skyBox.dds", true);
		std::thread thread3(&D3DMesh::CreateFromVectors, &plane, pDevice, &vert, &indicies[0], 6, 0, XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f), "resources/DesertTexture.dds");
		std::thread thread4(&D3DMesh::CreateFromVectors, &star, pDevice, &verticies[0], &indicies[0], 12, 60, XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f), nullptr);
		std::thread thread5(&D3DMesh::Initizalize, &odst, pDevice, "resources/ODST.obj", "resources/odst_body_diffuse.dds", false);

		//alien.Initizalize(pDevice, "resources/MPHybrid.obj", "resources/alienTexture.dds");
		thread1.join(); 
		thread2.join();
		thread3.join();
		thread4.join();
		thread5.join();


		//SkyBox.Initizalize(pDevice, "resources/Cube.obj", "resources/skyBox.dds", true);


	XMFLOAT4X4 tempCam;
	XMMATRIX mat = XMMatrixIdentity();
	XMStoreFloat4x4(&vsCBuffer1.world, mat);

	mat = XMMatrixPerspectiveFovLH(1.047197f, (BACKBUFFER_WIDTH / BACKBUFFER_HEIGHT), 0.1f, 1000.0f);


	XMStoreFloat4x4(&vsCBuffer1.projection, mat);

	mat = XMMatrixTranslation(0.0f, 15.0f, -25.0f);
	mat = XMMatrixInverse(&XMMatrixDeterminant(mat), mat);
	XMStoreFloat4x4(&tempCam, mat);
	

	mat = XMMatrixIdentity();
	XMStoreFloat4x4(&vsCBuffer3.world, mat);
	
	mat = XMMatrixPerspectiveFovLH(1.047197f, (BACKBUFFER_WIDTH / BACKBUFFER_HEIGHT), 0.1f, 1000.0f);
	XMStoreFloat4x4(&vsCBuffer3.projection, mat);

	camera1.SetMatrix(tempCam);

	mat = XMMatrixTranslation(0.0f, 10.0f, -5.0f);
	XMStoreFloat4x4(&vsCBuffer2.world, mat);
	
	mat = XMMatrixPerspectiveFovLH(1.047197f, (BACKBUFFER_WIDTH / BACKBUFFER_HEIGHT), 0.1f, 1000.0f);
	XMStoreFloat4x4(&vsCBuffer2.projection, mat);

	dlCBuffer.dlColor = { 0.5f, 0.5f, 0.5f, 1.0f };
	dlCBuffer.lightDirection = { 0.533882f, -1.0f, -0.310041f, 1.0f };


	plCBuffer.plColor = { 1.0f, 1.0f, 0.0f, 1.0f };
	plCBuffer.plPos = { 0.0f, 10.0f, -5.0f , 1.0f};

	slCBuffer.slColor = { 1.0f, 0.0f, 0.0f, 1.0f };
	slCBuffer.coneDir = {0.0f, 0.0f, 1.0f, 1.0f};
	slCBuffer.slPos = { 0.0f, 10.0f, 15.0f, 1.0f };
	//slCBuffer.coneR = { 0.9f, 0.9f, 0.9f, 0.9f };
	skyBuffer.projection = vsCBuffer1.projection;

	mat = XMMatrixTranslation(25.0f, 0.0f, 35.0f);
	XMStoreFloat4x4(&vsCBuffer4.world, mat);
	vsCBuffer4.projection = vsCBuffer1.projection;

}
bool  DX_APP::Run()
{
	timer.Signal();


//	MoveCamera(scene);
	pContext->OMSetRenderTargets(1, &pRenderTargetView, pDepthStencilView);
	pContext->OMSetDepthStencilState(depthStencilState, 1);


	pContext->RSSetViewports(1, &viewPort);
	FLOAT black[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
	
	pContext->ClearRenderTargetView(pRenderTargetView, black);
	pContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	//

	////DrawIndexedMesh(star, scene);
	////DrawMesh(p90, scene);
	camera1.MoveCamera(timer.Delta());

	vsCBuffer1.view = camera1.GetMatrix();
	vsCBuffer2.view = camera1.GetMatrix();
	vsCBuffer3.view = camera1.GetMatrix();
	vsCBuffer4.view = camera1.GetMatrix();
	skyBuffer.view = camera1.GetMatrix();
	

	pContext->RSSetState(CWCull);
	UpdateSkyBox();
	SkyBox.Render(pContext, &skyBoxShaders, &skyBuffer, &dlCBuffer, &plCBuffer, &slCBuffer, (float)timer.Delta());
	pContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
	pContext->RSSetState(CCWCull);
	UpdateFlashLight();
	camera1.RotateLight(&slCBuffer);


	//pContext->RSSetState(m_pRasterizerState);
	alien.Render(pContext, &shaderSet1, &vsCBuffer1, &dlCBuffer, &plCBuffer, &slCBuffer, (float)timer.Delta());
	//pContext->ClearState();
	RotateStar();

	odst.Render(pContext, &shaderSet1, &vsCBuffer4, &dlCBuffer, &plCBuffer, &slCBuffer, (float)timer.Delta());

	star.RenderIndexed(pContext, &starShaders, &vsCBuffer2, &dlCBuffer, &plCBuffer, &slCBuffer, (float)timer.Delta());
	plane.Render(pContext, &planeShaders, &vsCBuffer3, &dlCBuffer, &plCBuffer, &slCBuffer, (float)timer.Delta());

	pContext->ClearState();
	pSwapChain->Present(0, 0);

	return true;
}
bool  DX_APP::ShutDown()
{
	pContext->ClearState();

	pSwapChain->SetFullscreenState(FALSE, NULL);
	if (pSwapChain) pSwapChain->Release();

	if (pContext) pContext->Release();

	if (pDevice) pDevice->Release();

	if (CCWCull) CCWCull->Release();

	if (CWCull) CWCull->Release();

	if (pRenderTargetView) pRenderTargetView->Release();

	if(depthBuffer) depthBuffer->Release();
	if (pDepthStencilView) pDepthStencilView->Release();
	if (depthStencilState) depthStencilState->Release();
	if (instanceBuffer) instanceBuffer->Release();
	
	
	//if (m_pRasterizerState) m_pRasterizerState->Release();

	shaderSet1.ShutDown();
	starShaders.ShutDown();
	planeShaders.ShutDown();
	skyBoxShaders.ShutDown();
	alien.ShutDown();
	star.ShutDown();
	plane.ShutDown();
	SkyBox.ShutDown();
	odst.ShutDown();
	




	UnregisterClass(L"DirectXApplication", application);
	return true;
}



HRESULT DX_APP::CreateDepthStencilBuffer(void)
{

	D3D11_TEXTURE2D_DESC depthBuffDesc;
	ZeroMemory(&depthBuffDesc, sizeof(depthBuffDesc));
	depthBuffDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthBuffDesc.Format = DXGI_FORMAT_D32_FLOAT;
	depthBuffDesc.ArraySize = 1;
	depthBuffDesc.SampleDesc.Count = 8;
	depthBuffDesc.SampleDesc.Quality = 16;
	depthBuffDesc.Width = (UINT)BACKBUFFER_WIDTH;
	depthBuffDesc.Height = (UINT)BACKBUFFER_HEIGHT;
	depthBuffDesc.Usage = D3D11_USAGE_DEFAULT;
	depthBuffDesc.MipLevels = 1;
	HRESULT hr = pDevice->CreateTexture2D(&depthBuffDesc, NULL, &depthBuffer);

	
	//Depth Stencile State

	D3D11_DEPTH_STENCIL_DESC depthStencilDesc2;
	ZeroMemory(&depthStencilDesc2, sizeof(depthStencilDesc2));
	depthStencilDesc2.DepthEnable = true;
	depthStencilDesc2.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	depthStencilDesc2.DepthFunc = D3D11_COMPARISON_LESS;
	depthStencilDesc2.StencilEnable = true;
	depthStencilDesc2.StencilReadMask = 0xFF;
	depthStencilDesc2.StencilWriteMask = 0xFF;
	depthStencilDesc2.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc2.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	depthStencilDesc2.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc2.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	depthStencilDesc2.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc2.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
	depthStencilDesc2.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc2.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

	hr = pDevice->CreateDepthStencilState(&depthStencilDesc2, &depthStencilState);

	D3D11_DEPTH_STENCIL_VIEW_DESC DSViewDesc;
	ZeroMemory(&DSViewDesc, sizeof(DSViewDesc));
	DSViewDesc.Format = DXGI_FORMAT_D32_FLOAT;
	DSViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
	DSViewDesc.Texture2D.MipSlice = 0;
	




	hr = pDevice->CreateDepthStencilView(depthBuffer, // Depth stencil texture
		&DSViewDesc, // Depth stencil desc
		&pDepthStencilView);  // [out] Depth stencil view

	return hr;
}

void DX_APP::CreateStarInstanceBuffer(unsigned int numInstance)
{

}
void DX_APP::UpdateSkyBox()
{

	XMMATRIX mat = XMLoadFloat4x4(&camera1.GetMatrix());
	mat = XMMatrixInverse(NULL, mat);

	XMFLOAT3 pos;
	pos.x = mat.r[3].m128_f32[0];
	pos.y = mat.r[3].m128_f32[1];
	pos.z = mat.r[3].m128_f32[2];

	

	XMMATRIX world = XMMatrixTranslation(pos.x, pos.y, pos.z);

	XMStoreFloat4x4(&skyBuffer.world, world);
}

void DX_APP::UpdateFlashLight()
{
	XMMATRIX mat = XMLoadFloat4x4(&camera1.GetMatrix());
	mat = XMMatrixInverse(NULL, mat);

	
	slCBuffer.slPos.x = mat.r[3].m128_f32[0];
	slCBuffer.slPos.y = mat.r[3].m128_f32[1];
	slCBuffer.slPos.z = mat.r[3].m128_f32[2];
	slCBuffer.slPos.w = 1.0f;


}
void DX_APP::RotateStar()
{

	XMMATRIX world = XMLoadFloat4x4(&vsCBuffer2.world);

	XMMATRIX rotation = XMMatrixRotationY((float)timer.Delta());
	XMMATRIX rotationZ = XMMatrixRotationZ((float)timer.Delta()*2.0f);

	world = XMMatrixMultiply(rotationZ, world);

	world = XMMatrixMultiply(world, rotation);

	plCBuffer.plPos.x = world.r[3].m128_f32[0];
	plCBuffer.plPos.y = world.r[3].m128_f32[1];
	plCBuffer.plPos.z = world.r[3].m128_f32[2];
	plCBuffer.plPos.w = 1.0f;

	XMStoreFloat4x4(&vsCBuffer2.world, world);

	
}

